Quantifying effect of planting dates on potential potato yield

A crop modeling approach

Background

This R markdown notebook aims to document the simulation exercise performed by Ramírez et al. (2024). The SOLANUM model was used to determine the potential potato yield (Yp) and “roughly” quantify the effect of planting dates on Yp. The model was calibrated using the SOLANUM’s Parameter Estimator and fed with weather data from NASA POWER

SOLANUM model calibration

The SOLANUM model has a tool called Parameter Estimator which translates expert knowledge about the crop phenology into the model parameters. This tool is based on allometric and heuristic methods that relate mathematical functions of the vegetative (canopy cover) and reproductive (tuber partitioning) crop growth with the parameters of the model. This tool is based on the following 3 principles:

  • Use generic mathematical functions to describe either canopy cover or tuber formation, regardless of varieties or environmental conditions, but with specific parameters that vary depending on varieties.
  • Apply numerical methods to estimate specific parameters by forcing the function to fit a minimum number of data points.
  • The pre-defined minimum number of data points needed to fit the functions must be obtained from expert knowledge. This comprises sowing and harvest dates, day of emergence, day of the maximum canopy cover, maximum canopy cover index, day of physiological maturity, and day of tuber initiation.

Please see Harahagazwe et al. (2018). for a complete information about the Parameter Estimator tool.

Figura 1: Parameter Estimator tool of the SOLANUM model.
Figura 1: Parameter Estimator tool of the SOLANUM model.

The Parameter Estimated tool was run with . Values of the crop parameters for both varieties were saved in CropParamsList.Rdata. Let’s see them in the following R chunk code:

load(url("https://github.com/jninanya/Ramirez-et-al-2024/raw/main/solanumR/CropParamsList.Rdata"))

CropParamsList$BariAlu72
##   wmax     tm     te      A     tu      b    RUE    DMc 
##   0.90 330.00 870.00   0.75 650.00 190.00   3.22   0.20 
## attr(,"CropParamsInfo")
## [1] "wmax : Maximum canopy cover index (fraction)"                        
## [2] "tm   : Thermal time at the maximum canopy cover growth rate (C-day)" 
## [3] "te   : Thermal time at the maximum canopy cover value (C-day)"       
## [4] "A    : Maximum harvest index (fraction)"                             
## [5] "tu   : Thermal time at maximum tuber partition rate (C-day)"         
## [6] "b    : Thermal time just before the tuber initiation process (C-day)"
## [7] "RUE  : Average radiation use efficiency (g/MJ)"                      
## [8] "DMc  : Dry matter concentration of tubers (fraction)"                
## attr(,"VarietyName")
## [1] "BariAlu72"
CropParamsList$BariAlu78
##   wmax     tm     te      A     tu      b    RUE    DMc 
##   0.90 330.00 870.00   0.75 650.00 190.00   3.22   0.20 
## attr(,"CropParamsInfo")
## [1] "wmax : Maximum canopy cover index (fraction)"                        
## [2] "tm   : Thermal time at the maximum canopy cover growth rate (C-day)" 
## [3] "te   : Thermal time at the maximum canopy cover value (C-day)"       
## [4] "A    : Maximum harvest index (fraction)"                             
## [5] "tu   : Thermal time at maximum tuber partition rate (C-day)"         
## [6] "b    : Thermal time just before the tuber initiation process (C-day)"
## [7] "RUE  : Average radiation use efficiency (g/MJ)"                      
## [8] "DMc  : Dry matter concentration of tubers (fraction)"                
## attr(,"VarietyName")
## [1] "BariAlu78"
# load libraries
library(nasapower)
library(meteor)
library(lubridate)
library(dplyr)

load(url("https://github.com/jninanya/Ramirez-et-al-2024/raw/main/solanumR/CropParamsList.Rdata"))

CropParamsList$BariAlu72
##   wmax     tm     te      A     tu      b    RUE    DMc 
##   0.90 330.00 870.00   0.75 650.00 190.00   3.22   0.20 
## attr(,"CropParamsInfo")
## [1] "wmax : Maximum canopy cover index (fraction)"                        
## [2] "tm   : Thermal time at the maximum canopy cover growth rate (C-day)" 
## [3] "te   : Thermal time at the maximum canopy cover value (C-day)"       
## [4] "A    : Maximum harvest index (fraction)"                             
## [5] "tu   : Thermal time at maximum tuber partition rate (C-day)"         
## [6] "b    : Thermal time just before the tuber initiation process (C-day)"
## [7] "RUE  : Average radiation use efficiency (g/MJ)"                      
## [8] "DMc  : Dry matter concentration of tubers (fraction)"                
## attr(,"VarietyName")
## [1] "BariAlu72"
# defining variables (wvars) and period
wvars <- c("T2M_MAX", "T2M_MIN", "ALLSKY_SFC_SW_DWN")
period <- c("2000-01-01", "2023-12-31")

# coordinates (lon, lat) of the Fultola location
FUL <- c(89.5262, 22.7088) 

# getting daily data from NASA POWER 
wdata <- get_power(
  community = "ag",
  lonlat = FUL,
  pars = wvars,
  dates = period,
  temporal_api = "daily"
)

colnames(wdata)[8:10] <- c("TMAX", "TMIN", "SRAD")
wdata$photoperiod <- photoperiod(wdata$DOY, wdata$LAT)

#--- climatic conditions (average by DOY)
smr_mean <- wdata %>%
  group_by(DOY) %>%
  summarise_at(c("TMAX", "TMIN", "SRAD"), mean, na.rm = TRUE)

smr_q10 <- wdata %>%
  group_by(DOY) %>%
  summarise_at(c("TMAX", "TMIN", "SRAD"), quantile, probs = 0.10, na.rm = TRUE)

smr_q90 <- wdata %>%
  group_by(DOY) %>%
  summarise_at(c("TMAX", "TMIN", "SRAD"), quantile, probs = 0.90, na.rm = TRUE)


smr_mean <- smr_mean[1:365,]
smr_q10 <- smr_q10[1:365,]
smr_q90 <- smr_q90[1:365,]
x <- smr_mean$DOY


climate <- apply(smr_mean, 2, rep, 2)
climate <- as.data.frame(climate)

year1 = fromDoy(climate$DOY[1:365], 2021)
year2 = fromDoy(climate$DOY[366:730], 2022)

climate$day <- NULL
climate$month <- NULL
climate$year <- NULL
climate$day[1:365] <- day(year1)
climate$day[366:730] <- day(year2)
climate$month[1:365] <- month(year1)
climate$month[366:730] <- month(year2)
climate$year[1:365] <- year(year1)
climate$year[366:730] <- year(year2)

#--- plot for minimum and maximum temperature ----------------------------------
plot(x=0, y=0, type = "l", xlim = c(1,365), ylim = c(5,45), 
     xlab = "", ylab = "Temperature (°C)", axes = FALSE)
box()
axis(1, at = c(15,75,136,197,259,320), 
     labels = c("JAN","MAR","MAY","JUL","SEP","NOV"))
axis(2, las = 1)

#minimum temperature
polygon(c(x, rev(x)), 
        c(smr_q90$TMIN, rev(smr_q10$TMIN)), col = "#ffb2b2")
lines(x, smr_mean$TMIN, col = "#FF0000", lwd = 2)
lines(x, smr_q10$TMIN, col = "#ffb2b2")
lines(x, smr_q90$TMIN, col = "#ffb2b2")

#maximum temperature
polygon(c(x, rev(x)), 
        c(smr_q90$TMAX, rev(smr_q10$TMAX)), col = "#b2b2ff")
lines(x, smr_mean$TMAX, col = "#0000FF", lwd = 2)
lines(x, smr_q10$TMAX, col = "#b2b2ff")
lines(x, smr_q90$TMAX, col = "#b2b2ff")

legend(x = 300, y = 45, legend = c("TMIN", "TMAX"), 
       col = c("#FF0000", "#0000FF" ), lty = 1, cex = 0.8)

#--- plot for solar radiation --------------------------------------------------
plot(x=0, y=0, type = "l", xlim = c(1,365), ylim = c(0,30), 
     xlab = "", ylab = "Solar radiation (MJ/m2.day)", axes = FALSE)
box()
axis(1, at = c(15,75,136,197,259,320), 
     labels = c("JAN","MAR","MAY","JUL","SEP","NOV"))
axis(2, las = 1)
polygon(c(x, rev(x)), 
        c(smr_q90$SRAD, rev(smr_q10$SRAD)), col = "#e8cfb4")
lines(x, smr_mean$SRAD, col = "#B45F06", lwd = 2)
lines(x, smr_q10$SRAD, col = "#e8cfb4")
lines(x, smr_q90$SRAD, col = "#e8cfb4")

################################################################################
# 3. Thermal time computing
################################################################################
year0 = 2000
year1 = 2022
n <- as.Date("2023-01-31")-as.Date("2022-11-01")+1
m <- year1-year0+1

#wdata <- wdata[wdata$YEAR>=year0 & wdata$YEAR<=year1,]

out0 <- as.data.frame(matrix(nrow = n, ncol = m))
out1 <- as.data.frame(matrix(nrow = n, ncol = m))
out2 <- as.data.frame(matrix(nrow = n, ncol = m))
yy = seq(year0, year1, by = 1)

# load extra functions
source("https://raw.githubusercontent.com/jninanya/Ramirez-et-al-2024/main/solanumR/thermalTime.R")



weather <- wdata

#for(jj in 1:m){
#  
#  date0 = as.Date(paste0(yy[jj], "-11-01"))-1
#  
#  for(ii in 1:n){
#  
#    
#    sDate = as.Date(date0 + ii)
#    sDate.name = paste(month(sDate), day(sDate), sep = "-")
#    out0[ii, jj] = as.character(sDate)
#    
#    sowing = sDate
#    harvest = sowing + 90
#    ndays = as.numeric(harvest-sowing)+1
#    
### variety Bari Alu 72    
#    source("https://raw.githubusercontent.com/jninanya/Ramirez-et-a#l-2024/main/solanumR/BARI-Alu-72.R")
#    source("https://raw.githubusercontent.com/jninanya/Ramirez-et-a#l-2024/main/solanumR/Module_PotentialGrowth_V2.0.R")
#    
#    out1[ii, jj] = ifelse(df$fty[ndays]>20, df$fty[ndays], NA)
#    
### variety Bari Alu 78
#    source("https://raw.githubusercontent.com/jninanya/Ramirez-et-a#l-2024/main/solanumR/BARI-Alu-78.R")
#    source("https://raw.githubusercontent.com/jninanya/Ramirez-et-a#l-2024/main/solanumR/Module_PotentialGrowth_V2.0.R")
#    
#    out2[ii, jj] = ifelse(df$fty[ndays]>20, df$fty[ndays], NA)
#    
#    rownames(out0)[ii] = sDate.name
#    rownames(out1)[ii] = sDate.name
#    rownames(out2)[ii] = sDate.name
#  }
#  
#  colnames(out0)[jj] = yy[jj]
#  colnames(out1)[jj] = yy[jj]
#  colnames(out2)[jj] = yy[jj]
#  
#}

#d1 <- out1
#d2 <- out2
#
#boxplot(t(d1), col = "green", outline = FALSE, las=1,
#        ylab = "potential yield (t/ha)")
#
#boxplot(t(d2), col = "red", outline = FALSE, las=1,
#        ylab = "potential yield (t/ha)")
#
#
### plot fty by planting date
#x <- 1:92
#fty_mean <- apply(out1, 1, mean, na.rm = TRUE)
#fty_q10 <- apply(out1, 1, quantile, probs = 0.10, na.rm = TRUE)
#fty_q90 <- apply(out1, 1, quantile, probs = 0.90, na.rm = TRUE)
#
#
##--------------------------------------------------
##--------------------------------------------------
#### General figure settings
#par(oma = c(4, 1, 0.5, 0.5),  # general margins
#    mfrow = c(2, 1),              # number of sub-figures
#    mar = c(0,3,0,0),           # margins per sub-figure
##    ps = 10,                      # text font size
#    family = "serif"              # text family
##    lwd = 0.5,                    # line width
##    las = 1,                      # style of axis labels
##    pch = 20                      # plotting points
#)
#
#x=1:90
#y1=d1$`2021`[1:90]
#y2=d2$`2021`[1:90]
#plot(x, y1, type = "l", xlim = c(1,92), ylim = c(23,57), 
#     xlab = "", ylab = "potential yield (t/ha)", axes = FALSE, 
#     lwd = 2)
#box()
#lines(x, y2, lwd = 2, col = "gray50")
#
##axis(1, at = c(5,15,25,35,45,55,66,76,86), las = 2,
##     labels = c("5-nov","15-nov","25-nov","5-dec","15-dec","25-dec#","5-jan","15-jan","25-jan"))
##axis(1, las = 1, at = seq(5, 90, by=10))
#axis(2, las = 1, at=seq(25,55,by=5))
#abline(v=50, lty = 2, col = "blue")
#abline(v=74, lty = 2, col = "blue")
#
#text(47, 55.5, "ZT", col = "blue")
#text(71, 55.5, "CT", col = "blue")
#
#text(0, 55.5, expression(bold("A")))
#mtext(side=2, text=bquote("yield (t ha"^{"-1"}*")"), cex = 1.5, #line = 2.4)
#
####
#x=1:85
#y1=d1$`2022`[1:85]
#y2=d2$`2022`[1:85]
#plot(x, y1, type = "l", xlim = c(1,92), ylim = c(23,52), 
#     xlab = "", ylab = "potential yield (t/ha)", axes = FALSE, 
#     lwd = 2)
#box()
#lines(x, y2, lwd = 2, col = "gray50")
#
#axis(1, at = c(5,15,25,35,45,55,66,76,86), las = 2,
#     labels = c("05-nov","15-nov","25-nov","05-dec","15-dec","25-de#c","05-jan","15-jan","25-jan"))
##axis(1, las = 1, at = seq(5, 90, by=10))
#axis(2, las = 1, at=seq(25,50,by=5))
#abline(v=44, lty = 2, col = "blue")
#abline(v=62, lty = 2, col = "blue")
#
#text(41, 51, "ZT", col = "blue")
#text(59, 51, "CT", col = "blue")
#
#text(0, 51, expression(bold("B")))
#
#mtext(side=2, text=bquote("yield (t ha"^{"-1"}*")"), cex = 1.5, #line = 2.4)
#
LS0tDQp0aXRsZTogIlF1YW50aWZ5aW5nIGVmZmVjdCBvZiBwbGFudGluZyBkYXRlcyBvbiBwb3RlbnRpYWwgcG90YXRvIHlpZWxkIg0Kc3VidGl0bGU6ICJBIGNyb3AgbW9kZWxpbmcgYXBwcm9hY2giDQphdXRob3I6ICJKb2hhbiBOaW5hbnlhIChub25pKSINCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCINCiNzaXRlOiBib29rZG93bjo6Ym9va2Rvd25fc2l0ZQ0KI2RvY3VtZW50Y2xhc3M6IGJvb2sNCm91dHB1dDoNCiAgcm1kZm9ybWF0czo6cmVhZHRoZWRvd246DQogICAgaGlnaGxpZ2h0OiBrYXRlDQogICAgbnVtYmVyX3NlY3Rpb25zOiBGQUxTRQ0KICAgIGNvZGVfZm9sZGluZzogc2hvdw0KICAgIGNvZGVfZG93bmxvYWQ6IFRSVUUNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UpDQpgYGANCg0KIyMgQmFja2dyb3VuZA0KDQpUaGlzIFIgbWFya2Rvd24gbm90ZWJvb2sgYWltcyB0byBkb2N1bWVudCB0aGUgc2ltdWxhdGlvbiBleGVyY2lzZSBwZXJmb3JtZWQgYnkgUmFtw61yZXogZXQgYWwuICgyMDI0KS4gVGhlIFtTT0xBTlVNXShodHRwczovL2NpcG90YXRvLm9yZy9zaXRlL2lucm0vaG9tZS9kb3dubW9kLmh0bSkgbW9kZWwgd2FzIHVzZWQgdG8gZGV0ZXJtaW5lIHRoZSBwb3RlbnRpYWwgcG90YXRvIHlpZWxkIChZcCkgYW5kICJyb3VnaGx5IiBxdWFudGlmeSB0aGUgZWZmZWN0IG9mIHBsYW50aW5nIGRhdGVzIG9uIFlwLiBUaGUgbW9kZWwgd2FzIGNhbGlicmF0ZWQgdXNpbmcgdGhlIFtTT0xBTlVNJ3MgUGFyYW1ldGVyIEVzdGltYXRvcl0oaHR0cHM6Ly9kb2kub3JnLzEwLjE1MTUvb3BhZy0yMDE4LTAwMTkpIGFuZCBmZWQgd2l0aCB3ZWF0aGVyIGRhdGEgZnJvbSBbTkFTQSBQT1dFUl0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL25hc2Fwb3dlci9pbmRleC5odG1sKQ0KDQojIyBTT0xBTlVNIG1vZGVsIGNhbGlicmF0aW9uDQoNClRoZSBTT0xBTlVNIG1vZGVsIGhhcyBhIHRvb2wgY2FsbGVkICoqUGFyYW1ldGVyIEVzdGltYXRvcioqIHdoaWNoIHRyYW5zbGF0ZXMgZXhwZXJ0IGtub3dsZWRnZSBhYm91dCB0aGUgY3JvcCBwaGVub2xvZ3kgaW50byB0aGUgbW9kZWwgcGFyYW1ldGVycy4gVGhpcyB0b29sIGlzIGJhc2VkIG9uIGFsbG9tZXRyaWMgYW5kIGhldXJpc3RpYyBtZXRob2RzIHRoYXQgcmVsYXRlIG1hdGhlbWF0aWNhbCBmdW5jdGlvbnMgb2YgdGhlIHZlZ2V0YXRpdmUgKGNhbm9weSBjb3ZlcikgYW5kIHJlcHJvZHVjdGl2ZSAodHViZXIgcGFydGl0aW9uaW5nKSBjcm9wIGdyb3d0aCB3aXRoIHRoZSBwYXJhbWV0ZXJzIG9mIHRoZSBtb2RlbC4gXHRleHRjb2xvcntyZWR9e2JsdWV9IFRoaXMgdG9vbCBpcyBiYXNlZCBvbiB0aGUgZm9sbG93aW5nIDMgcHJpbmNpcGxlczoNCg0KKiBVc2UgZ2VuZXJpYyBtYXRoZW1hdGljYWwgZnVuY3Rpb25zIHRvIGRlc2NyaWJlIGVpdGhlciBjYW5vcHkgY292ZXIgb3IgdHViZXIgZm9ybWF0aW9uLCByZWdhcmRsZXNzIG9mIHZhcmlldGllcyBvciBlbnZpcm9ubWVudGFsIGNvbmRpdGlvbnMsIGJ1dCB3aXRoIHNwZWNpZmljIHBhcmFtZXRlcnMgdGhhdCB2YXJ5IGRlcGVuZGluZyBvbiB2YXJpZXRpZXMuDQoqIEFwcGx5IG51bWVyaWNhbCBtZXRob2RzIHRvIGVzdGltYXRlIHNwZWNpZmljIHBhcmFtZXRlcnMgYnkgZm9yY2luZyB0aGUgZnVuY3Rpb24gdG8gZml0IGEgbWluaW11bSBudW1iZXIgb2YgZGF0YSBwb2ludHMuDQoqIFRoZSBwcmUtZGVmaW5lZCBtaW5pbXVtIG51bWJlciBvZiBkYXRhIHBvaW50cyBuZWVkZWQgdG8gZml0IHRoZSBmdW5jdGlvbnMgbXVzdCBiZSBvYnRhaW5lZCBmcm9tIGV4cGVydCBrbm93bGVkZ2UuIFRoaXMgY29tcHJpc2VzIHNvd2luZyBhbmQgaGFydmVzdCBkYXRlcywgZGF5IG9mIGVtZXJnZW5jZSwgZGF5IG9mIHRoZSBtYXhpbXVtIGNhbm9weSBjb3ZlciwgbWF4aW11bSBjYW5vcHkgY292ZXIgaW5kZXgsIGRheSBvZiBwaHlzaW9sb2dpY2FsIG1hdHVyaXR5LCBhbmQgZGF5IG9mIHR1YmVyIGluaXRpYXRpb24uDQoNClBsZWFzZSBzZWUgW0hhcmFoYWdhendlIGV0IGFsLiAoMjAxOCkuXSgjSGFyYWhhZ2F6d2UtZXQtYWwtMjAxOCkgZm9yIGEgY29tcGxldGUgaW5mb3JtYXRpb24gYWJvdXQgdGhlIFBhcmFtZXRlciBFc3RpbWF0b3IgdG9vbC4NCg0KPCEtLSBGaWd1cmUgMDEgLS0+DQo8YSBpZD0iRmlndXJlMDEiPjwvYT4NCjxkaXYgc3R5bGU9InRleHQtYWxpZ246Y2VudGVyOyI+DQogICFbKipGaWd1cmEgMToqKiBQYXJhbWV0ZXIgRXN0aW1hdG9yIHRvb2wgb2YgdGhlIFNPTEFOVU0gbW9kZWwuXShodHRwczovL2dpdGh1Yi5jb20vam5pbmFueWEvUmFtaXJlei1ldC1hbC0yMDI0L2Jsb2IvbWFpbi9zb2xhbnVtUi9GaWd1cmVzL1BhcmFtZXRlci1Fc3RpbWF0b3ItVG9vbC5wbmc/cmF3PXRydWUpe3dpZHRoPTgxJX0NCiAgPHAgc3R5bGU9Im1hcmdpbi1ib3R0b206IDEwcHg7Ij48L3A+IDwhLS0gQWRkIHNvbWUgc3BhY2UgYmVsb3cgdGhlIGltYWdlIGNhcHRpb24gLS0+DQo8L2Rpdj4NCg0KVGhlIFBhcmFtZXRlciBFc3RpbWF0ZWQgdG9vbCB3YXMgcnVuIHdpdGggLiBWYWx1ZXMgb2YgdGhlIGNyb3AgcGFyYW1ldGVycyBmb3IgYm90aCB2YXJpZXRpZXMgd2VyZSBzYXZlZCBpbiBgQ3JvcFBhcmFtc0xpc3QuUmRhdGFgLiBMZXQncyBzZWUgdGhlbSBpbiB0aGUgZm9sbG93aW5nIFIgY2h1bmsgY29kZToNCg0KYGBge3IgY3JvcC1wYXJhbWV0ZXJzLWRhdGFiYXNlLCByZXN1bHRzPSdzaG93JywgY29sbGFwc2UgPSBUUlVFfQ0KbG9hZCh1cmwoImh0dHBzOi8vZ2l0aHViLmNvbS9qbmluYW55YS9SYW1pcmV6LWV0LWFsLTIwMjQvcmF3L21haW4vc29sYW51bVIvQ3JvcFBhcmFtc0xpc3QuUmRhdGEiKSkNCg0KQ3JvcFBhcmFtc0xpc3QkQmFyaUFsdTcyDQpDcm9wUGFyYW1zTGlzdCRCYXJpQWx1NzgNCmBgYA0KDQoNCg0KYGBge3J9DQojIGxvYWQgbGlicmFyaWVzDQpsaWJyYXJ5KG5hc2Fwb3dlcikNCmxpYnJhcnkobWV0ZW9yKQ0KbGlicmFyeShsdWJyaWRhdGUpDQpsaWJyYXJ5KGRwbHlyKQ0KDQpsb2FkKHVybCgiaHR0cHM6Ly9naXRodWIuY29tL2puaW5hbnlhL1JhbWlyZXotZXQtYWwtMjAyNC9yYXcvbWFpbi9zb2xhbnVtUi9Dcm9wUGFyYW1zTGlzdC5SZGF0YSIpKQ0KDQpDcm9wUGFyYW1zTGlzdCRCYXJpQWx1NzINCg0KIyBkZWZpbmluZyB2YXJpYWJsZXMgKHd2YXJzKSBhbmQgcGVyaW9kDQp3dmFycyA8LSBjKCJUMk1fTUFYIiwgIlQyTV9NSU4iLCAiQUxMU0tZX1NGQ19TV19EV04iKQ0KcGVyaW9kIDwtIGMoIjIwMDAtMDEtMDEiLCAiMjAyMy0xMi0zMSIpDQoNCiMgY29vcmRpbmF0ZXMgKGxvbiwgbGF0KSBvZiB0aGUgRnVsdG9sYSBsb2NhdGlvbg0KRlVMIDwtIGMoODkuNTI2MiwgMjIuNzA4OCkgDQoNCiMgZ2V0dGluZyBkYWlseSBkYXRhIGZyb20gTkFTQSBQT1dFUiANCndkYXRhIDwtIGdldF9wb3dlcigNCiAgY29tbXVuaXR5ID0gImFnIiwNCiAgbG9ubGF0ID0gRlVMLA0KICBwYXJzID0gd3ZhcnMsDQogIGRhdGVzID0gcGVyaW9kLA0KICB0ZW1wb3JhbF9hcGkgPSAiZGFpbHkiDQopDQoNCmNvbG5hbWVzKHdkYXRhKVs4OjEwXSA8LSBjKCJUTUFYIiwgIlRNSU4iLCAiU1JBRCIpDQp3ZGF0YSRwaG90b3BlcmlvZCA8LSBwaG90b3BlcmlvZCh3ZGF0YSRET1ksIHdkYXRhJExBVCkNCg0KIy0tLSBjbGltYXRpYyBjb25kaXRpb25zIChhdmVyYWdlIGJ5IERPWSkNCnNtcl9tZWFuIDwtIHdkYXRhICU+JQ0KICBncm91cF9ieShET1kpICU+JQ0KICBzdW1tYXJpc2VfYXQoYygiVE1BWCIsICJUTUlOIiwgIlNSQUQiKSwgbWVhbiwgbmEucm0gPSBUUlVFKQ0KDQpzbXJfcTEwIDwtIHdkYXRhICU+JQ0KICBncm91cF9ieShET1kpICU+JQ0KICBzdW1tYXJpc2VfYXQoYygiVE1BWCIsICJUTUlOIiwgIlNSQUQiKSwgcXVhbnRpbGUsIHByb2JzID0gMC4xMCwgbmEucm0gPSBUUlVFKQ0KDQpzbXJfcTkwIDwtIHdkYXRhICU+JQ0KICBncm91cF9ieShET1kpICU+JQ0KICBzdW1tYXJpc2VfYXQoYygiVE1BWCIsICJUTUlOIiwgIlNSQUQiKSwgcXVhbnRpbGUsIHByb2JzID0gMC45MCwgbmEucm0gPSBUUlVFKQ0KDQoNCnNtcl9tZWFuIDwtIHNtcl9tZWFuWzE6MzY1LF0NCnNtcl9xMTAgPC0gc21yX3ExMFsxOjM2NSxdDQpzbXJfcTkwIDwtIHNtcl9xOTBbMTozNjUsXQ0KeCA8LSBzbXJfbWVhbiRET1kNCg0KDQpjbGltYXRlIDwtIGFwcGx5KHNtcl9tZWFuLCAyLCByZXAsIDIpDQpjbGltYXRlIDwtIGFzLmRhdGEuZnJhbWUoY2xpbWF0ZSkNCg0KeWVhcjEgPSBmcm9tRG95KGNsaW1hdGUkRE9ZWzE6MzY1XSwgMjAyMSkNCnllYXIyID0gZnJvbURveShjbGltYXRlJERPWVszNjY6NzMwXSwgMjAyMikNCg0KY2xpbWF0ZSRkYXkgPC0gTlVMTA0KY2xpbWF0ZSRtb250aCA8LSBOVUxMDQpjbGltYXRlJHllYXIgPC0gTlVMTA0KY2xpbWF0ZSRkYXlbMTozNjVdIDwtIGRheSh5ZWFyMSkNCmNsaW1hdGUkZGF5WzM2Njo3MzBdIDwtIGRheSh5ZWFyMikNCmNsaW1hdGUkbW9udGhbMTozNjVdIDwtIG1vbnRoKHllYXIxKQ0KY2xpbWF0ZSRtb250aFszNjY6NzMwXSA8LSBtb250aCh5ZWFyMikNCmNsaW1hdGUkeWVhclsxOjM2NV0gPC0geWVhcih5ZWFyMSkNCmNsaW1hdGUkeWVhclszNjY6NzMwXSA8LSB5ZWFyKHllYXIyKQ0KDQojLS0tIHBsb3QgZm9yIG1pbmltdW0gYW5kIG1heGltdW0gdGVtcGVyYXR1cmUgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KcGxvdCh4PTAsIHk9MCwgdHlwZSA9ICJsIiwgeGxpbSA9IGMoMSwzNjUpLCB5bGltID0gYyg1LDQ1KSwgDQogICAgIHhsYWIgPSAiIiwgeWxhYiA9ICJUZW1wZXJhdHVyZSAowrBDKSIsIGF4ZXMgPSBGQUxTRSkNCmJveCgpDQpheGlzKDEsIGF0ID0gYygxNSw3NSwxMzYsMTk3LDI1OSwzMjApLCANCiAgICAgbGFiZWxzID0gYygiSkFOIiwiTUFSIiwiTUFZIiwiSlVMIiwiU0VQIiwiTk9WIikpDQpheGlzKDIsIGxhcyA9IDEpDQoNCiNtaW5pbXVtIHRlbXBlcmF0dXJlDQpwb2x5Z29uKGMoeCwgcmV2KHgpKSwgDQogICAgICAgIGMoc21yX3E5MCRUTUlOLCByZXYoc21yX3ExMCRUTUlOKSksIGNvbCA9ICIjZmZiMmIyIikNCmxpbmVzKHgsIHNtcl9tZWFuJFRNSU4sIGNvbCA9ICIjRkYwMDAwIiwgbHdkID0gMikNCmxpbmVzKHgsIHNtcl9xMTAkVE1JTiwgY29sID0gIiNmZmIyYjIiKQ0KbGluZXMoeCwgc21yX3E5MCRUTUlOLCBjb2wgPSAiI2ZmYjJiMiIpDQoNCiNtYXhpbXVtIHRlbXBlcmF0dXJlDQpwb2x5Z29uKGMoeCwgcmV2KHgpKSwgDQogICAgICAgIGMoc21yX3E5MCRUTUFYLCByZXYoc21yX3ExMCRUTUFYKSksIGNvbCA9ICIjYjJiMmZmIikNCmxpbmVzKHgsIHNtcl9tZWFuJFRNQVgsIGNvbCA9ICIjMDAwMEZGIiwgbHdkID0gMikNCmxpbmVzKHgsIHNtcl9xMTAkVE1BWCwgY29sID0gIiNiMmIyZmYiKQ0KbGluZXMoeCwgc21yX3E5MCRUTUFYLCBjb2wgPSAiI2IyYjJmZiIpDQoNCmxlZ2VuZCh4ID0gMzAwLCB5ID0gNDUsIGxlZ2VuZCA9IGMoIlRNSU4iLCAiVE1BWCIpLCANCiAgICAgICBjb2wgPSBjKCIjRkYwMDAwIiwgIiMwMDAwRkYiICksIGx0eSA9IDEsIGNleCA9IDAuOCkNCg0KIy0tLSBwbG90IGZvciBzb2xhciByYWRpYXRpb24gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCnBsb3QoeD0wLCB5PTAsIHR5cGUgPSAibCIsIHhsaW0gPSBjKDEsMzY1KSwgeWxpbSA9IGMoMCwzMCksIA0KICAgICB4bGFiID0gIiIsIHlsYWIgPSAiU29sYXIgcmFkaWF0aW9uIChNSi9tMi5kYXkpIiwgYXhlcyA9IEZBTFNFKQ0KYm94KCkNCmF4aXMoMSwgYXQgPSBjKDE1LDc1LDEzNiwxOTcsMjU5LDMyMCksIA0KICAgICBsYWJlbHMgPSBjKCJKQU4iLCJNQVIiLCJNQVkiLCJKVUwiLCJTRVAiLCJOT1YiKSkNCmF4aXMoMiwgbGFzID0gMSkNCnBvbHlnb24oYyh4LCByZXYoeCkpLCANCiAgICAgICAgYyhzbXJfcTkwJFNSQUQsIHJldihzbXJfcTEwJFNSQUQpKSwgY29sID0gIiNlOGNmYjQiKQ0KbGluZXMoeCwgc21yX21lYW4kU1JBRCwgY29sID0gIiNCNDVGMDYiLCBsd2QgPSAyKQ0KbGluZXMoeCwgc21yX3ExMCRTUkFELCBjb2wgPSAiI2U4Y2ZiNCIpDQpsaW5lcyh4LCBzbXJfcTkwJFNSQUQsIGNvbCA9ICIjZThjZmI0IikNCg0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCiMgMy4gVGhlcm1hbCB0aW1lIGNvbXB1dGluZw0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCnllYXIwID0gMjAwMA0KeWVhcjEgPSAyMDIyDQpuIDwtIGFzLkRhdGUoIjIwMjMtMDEtMzEiKS1hcy5EYXRlKCIyMDIyLTExLTAxIikrMQ0KbSA8LSB5ZWFyMS15ZWFyMCsxDQoNCiN3ZGF0YSA8LSB3ZGF0YVt3ZGF0YSRZRUFSPj15ZWFyMCAmIHdkYXRhJFlFQVI8PXllYXIxLF0NCg0Kb3V0MCA8LSBhcy5kYXRhLmZyYW1lKG1hdHJpeChucm93ID0gbiwgbmNvbCA9IG0pKQ0Kb3V0MSA8LSBhcy5kYXRhLmZyYW1lKG1hdHJpeChucm93ID0gbiwgbmNvbCA9IG0pKQ0Kb3V0MiA8LSBhcy5kYXRhLmZyYW1lKG1hdHJpeChucm93ID0gbiwgbmNvbCA9IG0pKQ0KeXkgPSBzZXEoeWVhcjAsIHllYXIxLCBieSA9IDEpDQoNCiMgbG9hZCBleHRyYSBmdW5jdGlvbnMNCnNvdXJjZSgiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2puaW5hbnlhL1JhbWlyZXotZXQtYWwtMjAyNC9tYWluL3NvbGFudW1SL3RoZXJtYWxUaW1lLlIiKQ0KDQoNCg0Kd2VhdGhlciA8LSB3ZGF0YQ0KDQojZm9yKGpqIGluIDE6bSl7DQojICANCiMgIGRhdGUwID0gYXMuRGF0ZShwYXN0ZTAoeXlbampdLCAiLTExLTAxIikpLTENCiMgIA0KIyAgZm9yKGlpIGluIDE6bil7DQojICANCiMgICAgDQojICAgIHNEYXRlID0gYXMuRGF0ZShkYXRlMCArIGlpKQ0KIyAgICBzRGF0ZS5uYW1lID0gcGFzdGUobW9udGgoc0RhdGUpLCBkYXkoc0RhdGUpLCBzZXAgPSAiLSIpDQojICAgIG91dDBbaWksIGpqXSA9IGFzLmNoYXJhY3RlcihzRGF0ZSkNCiMgICAgDQojICAgIHNvd2luZyA9IHNEYXRlDQojICAgIGhhcnZlc3QgPSBzb3dpbmcgKyA5MA0KIyAgICBuZGF5cyA9IGFzLm51bWVyaWMoaGFydmVzdC1zb3dpbmcpKzENCiMgICAgDQojIyMgdmFyaWV0eSBCYXJpIEFsdSA3MiAgICANCiMgICAgc291cmNlKCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vam5pbmFueWEvUmFtaXJlei1ldC1hI2wtMjAyNC9tYWluL3NvbGFudW1SL0JBUkktQWx1LTcyLlIiKQ0KIyAgICBzb3VyY2UoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9qbmluYW55YS9SYW1pcmV6LWV0LWEjbC0yMDI0L21haW4vc29sYW51bVIvTW9kdWxlX1BvdGVudGlhbEdyb3d0aF9WMi4wLlIiKQ0KIyAgICANCiMgICAgb3V0MVtpaSwgampdID0gaWZlbHNlKGRmJGZ0eVtuZGF5c10+MjAsIGRmJGZ0eVtuZGF5c10sIE5BKQ0KIyAgICANCiMjIyB2YXJpZXR5IEJhcmkgQWx1IDc4DQojICAgIHNvdXJjZSgiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2puaW5hbnlhL1JhbWlyZXotZXQtYSNsLTIwMjQvbWFpbi9zb2xhbnVtUi9CQVJJLUFsdS03OC5SIikNCiMgICAgc291cmNlKCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vam5pbmFueWEvUmFtaXJlei1ldC1hI2wtMjAyNC9tYWluL3NvbGFudW1SL01vZHVsZV9Qb3RlbnRpYWxHcm93dGhfVjIuMC5SIikNCiMgICAgDQojICAgIG91dDJbaWksIGpqXSA9IGlmZWxzZShkZiRmdHlbbmRheXNdPjIwLCBkZiRmdHlbbmRheXNdLCBOQSkNCiMgICAgDQojICAgIHJvd25hbWVzKG91dDApW2lpXSA9IHNEYXRlLm5hbWUNCiMgICAgcm93bmFtZXMob3V0MSlbaWldID0gc0RhdGUubmFtZQ0KIyAgICByb3duYW1lcyhvdXQyKVtpaV0gPSBzRGF0ZS5uYW1lDQojICB9DQojICANCiMgIGNvbG5hbWVzKG91dDApW2pqXSA9IHl5W2pqXQ0KIyAgY29sbmFtZXMob3V0MSlbampdID0geXlbampdDQojICBjb2xuYW1lcyhvdXQyKVtqal0gPSB5eVtqal0NCiMgIA0KI30NCg0KI2QxIDwtIG91dDENCiNkMiA8LSBvdXQyDQojDQojYm94cGxvdCh0KGQxKSwgY29sID0gImdyZWVuIiwgb3V0bGluZSA9IEZBTFNFLCBsYXM9MSwNCiMgICAgICAgIHlsYWIgPSAicG90ZW50aWFsIHlpZWxkICh0L2hhKSIpDQojDQojYm94cGxvdCh0KGQyKSwgY29sID0gInJlZCIsIG91dGxpbmUgPSBGQUxTRSwgbGFzPTEsDQojICAgICAgICB5bGFiID0gInBvdGVudGlhbCB5aWVsZCAodC9oYSkiKQ0KIw0KIw0KIyMjIHBsb3QgZnR5IGJ5IHBsYW50aW5nIGRhdGUNCiN4IDwtIDE6OTINCiNmdHlfbWVhbiA8LSBhcHBseShvdXQxLCAxLCBtZWFuLCBuYS5ybSA9IFRSVUUpDQojZnR5X3ExMCA8LSBhcHBseShvdXQxLCAxLCBxdWFudGlsZSwgcHJvYnMgPSAwLjEwLCBuYS5ybSA9IFRSVUUpDQojZnR5X3E5MCA8LSBhcHBseShvdXQxLCAxLCBxdWFudGlsZSwgcHJvYnMgPSAwLjkwLCBuYS5ybSA9IFRSVUUpDQojDQojDQojIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIyMjIEdlbmVyYWwgZmlndXJlIHNldHRpbmdzDQojcGFyKG9tYSA9IGMoNCwgMSwgMC41LCAwLjUpLCAgIyBnZW5lcmFsIG1hcmdpbnMNCiMgICAgbWZyb3cgPSBjKDIsIDEpLCAgICAgICAgICAgICAgIyBudW1iZXIgb2Ygc3ViLWZpZ3VyZXMNCiMgICAgbWFyID0gYygwLDMsMCwwKSwgICAgICAgICAgICMgbWFyZ2lucyBwZXIgc3ViLWZpZ3VyZQ0KIyMgICAgcHMgPSAxMCwgICAgICAgICAgICAgICAgICAgICAgIyB0ZXh0IGZvbnQgc2l6ZQ0KIyAgICBmYW1pbHkgPSAic2VyaWYiICAgICAgICAgICAgICAjIHRleHQgZmFtaWx5DQojIyAgICBsd2QgPSAwLjUsICAgICAgICAgICAgICAgICAgICAjIGxpbmUgd2lkdGgNCiMjICAgIGxhcyA9IDEsICAgICAgICAgICAgICAgICAgICAgICMgc3R5bGUgb2YgYXhpcyBsYWJlbHMNCiMjICAgIHBjaCA9IDIwICAgICAgICAgICAgICAgICAgICAgICMgcGxvdHRpbmcgcG9pbnRzDQojKQ0KIw0KI3g9MTo5MA0KI3kxPWQxJGAyMDIxYFsxOjkwXQ0KI3kyPWQyJGAyMDIxYFsxOjkwXQ0KI3Bsb3QoeCwgeTEsIHR5cGUgPSAibCIsIHhsaW0gPSBjKDEsOTIpLCB5bGltID0gYygyMyw1NyksIA0KIyAgICAgeGxhYiA9ICIiLCB5bGFiID0gInBvdGVudGlhbCB5aWVsZCAodC9oYSkiLCBheGVzID0gRkFMU0UsIA0KIyAgICAgbHdkID0gMikNCiNib3goKQ0KI2xpbmVzKHgsIHkyLCBsd2QgPSAyLCBjb2wgPSAiZ3JheTUwIikNCiMNCiMjYXhpcygxLCBhdCA9IGMoNSwxNSwyNSwzNSw0NSw1NSw2Niw3Niw4NiksIGxhcyA9IDIsDQojIyAgICAgbGFiZWxzID0gYygiNS1ub3YiLCIxNS1ub3YiLCIyNS1ub3YiLCI1LWRlYyIsIjE1LWRlYyIsIjI1LWRlYyMiLCI1LWphbiIsIjE1LWphbiIsIjI1LWphbiIpKQ0KIyNheGlzKDEsIGxhcyA9IDEsIGF0ID0gc2VxKDUsIDkwLCBieT0xMCkpDQojYXhpcygyLCBsYXMgPSAxLCBhdD1zZXEoMjUsNTUsYnk9NSkpDQojYWJsaW5lKHY9NTAsIGx0eSA9IDIsIGNvbCA9ICJibHVlIikNCiNhYmxpbmUodj03NCwgbHR5ID0gMiwgY29sID0gImJsdWUiKQ0KIw0KI3RleHQoNDcsIDU1LjUsICJaVCIsIGNvbCA9ICJibHVlIikNCiN0ZXh0KDcxLCA1NS41LCAiQ1QiLCBjb2wgPSAiYmx1ZSIpDQojDQojdGV4dCgwLCA1NS41LCBleHByZXNzaW9uKGJvbGQoIkEiKSkpDQojbXRleHQoc2lkZT0yLCB0ZXh0PWJxdW90ZSgieWllbGQgKHQgaGEiXnsiLTEifSoiKSIpLCBjZXggPSAxLjUsICNsaW5lID0gMi40KQ0KIw0KIyMjIw0KI3g9MTo4NQ0KI3kxPWQxJGAyMDIyYFsxOjg1XQ0KI3kyPWQyJGAyMDIyYFsxOjg1XQ0KI3Bsb3QoeCwgeTEsIHR5cGUgPSAibCIsIHhsaW0gPSBjKDEsOTIpLCB5bGltID0gYygyMyw1MiksIA0KIyAgICAgeGxhYiA9ICIiLCB5bGFiID0gInBvdGVudGlhbCB5aWVsZCAodC9oYSkiLCBheGVzID0gRkFMU0UsIA0KIyAgICAgbHdkID0gMikNCiNib3goKQ0KI2xpbmVzKHgsIHkyLCBsd2QgPSAyLCBjb2wgPSAiZ3JheTUwIikNCiMNCiNheGlzKDEsIGF0ID0gYyg1LDE1LDI1LDM1LDQ1LDU1LDY2LDc2LDg2KSwgbGFzID0gMiwNCiMgICAgIGxhYmVscyA9IGMoIjA1LW5vdiIsIjE1LW5vdiIsIjI1LW5vdiIsIjA1LWRlYyIsIjE1LWRlYyIsIjI1LWRlI2MiLCIwNS1qYW4iLCIxNS1qYW4iLCIyNS1qYW4iKSkNCiMjYXhpcygxLCBsYXMgPSAxLCBhdCA9IHNlcSg1LCA5MCwgYnk9MTApKQ0KI2F4aXMoMiwgbGFzID0gMSwgYXQ9c2VxKDI1LDUwLGJ5PTUpKQ0KI2FibGluZSh2PTQ0LCBsdHkgPSAyLCBjb2wgPSAiYmx1ZSIpDQojYWJsaW5lKHY9NjIsIGx0eSA9IDIsIGNvbCA9ICJibHVlIikNCiMNCiN0ZXh0KDQxLCA1MSwgIlpUIiwgY29sID0gImJsdWUiKQ0KI3RleHQoNTksIDUxLCAiQ1QiLCBjb2wgPSAiYmx1ZSIpDQojDQojdGV4dCgwLCA1MSwgZXhwcmVzc2lvbihib2xkKCJCIikpKQ0KIw0KI210ZXh0KHNpZGU9MiwgdGV4dD1icXVvdGUoInlpZWxkICh0IGhhIl57Ii0xIn0qIikiKSwgY2V4ID0gMS41LCAjbGluZSA9IDIuNCkNCiMNCg0KDQoNCg0KDQoNCg0KDQoNCmBgYA0KDQoNCiMjIFJlZmVyZW5jZXMNCg0KMS4gW0hhcmFoYWdhendlIGV0IGFsLiAoMjAxOCldKCNIYXJhaGFnYXp3ZS1ldC1hbC0yMDE4KQ0K